﻿using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Util.QuartzNet
{
    public class QuartzHelper
    {
        #region 字段/属性

        private static QuartzHelper _quartzHelper = new QuartzHelper();
        public static QuartzHelper Instance
        {
            get
            {
                return QuartzHelper._quartzHelper;
            }
        }

        private static ISchedulerFactory _schedulerFactory;
        private static IScheduler _scheduler;

        #endregion

        #region 构造函数

        private QuartzHelper()
        {
            //初始化调试工厂
            var config = new NameValueCollection();
            config.Add("quartz.jobStore.misfireThreshold", "1000");
            QuartzHelper._schedulerFactory = new StdSchedulerFactory(config);
            //获得调度器
            QuartzHelper._scheduler = QuartzHelper._schedulerFactory.GetScheduler().Result;
            //调度开始
            QuartzHelper._scheduler.Start();
        }

        #endregion

        #region 方法

        /// <summary>
        /// 添加Job
        /// </summary>
        /// <typeparam name="T">泛型类</typeparam>
        /// <param name="jobName">job名称</param>
        /// <param name="groupName">分组名称</param>
        /// <param name="desc">描述内容</param>
        /// <param name="cronExpression">corn表达式</param>
        public async Task AddJob<T>(string jobName, string groupName, string desc, string cronExpression)
            where T: IJob
        {
            //判断 cron 表达式是否有效
            if (!IsValidExpression(cronExpression))
            {
                throw new Exception("请输入cron表达式");
            }

            //创建Job
            var job = JobBuilder.Create<T>()
                .WithIdentity(jobName, groupName)//键值
                .WithDescription(desc)//描述
                .Build();

            //创建触发器
            var trigger = TriggerBuilder.Create()
                .WithIdentity(jobName, groupName)//键值
                .WithDescription(desc)//描述
                .WithCronSchedule(cronExpression)//调度频率
                .Build();

            ((CronTriggerImpl)trigger).MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;
            //开始以创建的触发器调度创建的Job
            await _scheduler.ScheduleJob(job, trigger);
        }

        /// <summary>
        /// 以SimpleTrigger添加Job
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName">作业名称</param>
        /// <param name="groupName">分组名称</param>
        /// <param name="desc">作业描述</param>
        /// <param name="intervalType">间隔类型</param>
        /// <param name="interval">间隔值，intervalType为TimeSpan 则interval的值为ticks</param>
        /// <param name="misFireType">失败处理类型</param>
        /// <param name="startAt">开始时间点</param>
        /// <param name="endAt">结束时间点</param>
        /// <param name="repeatCount">重复多少次，为null 或小于0的数表示永久, 这个重复次数不包括启动后执行的一次，也就是说最终的执行次数为n+1</param>
        /// <returns></returns>
        public async Task AddJob<T>(string jobName, string groupName, string desc, IntervalType? intervalType, int interval, MisFireType? misFireType, DateTimeOffset? startAt, DateTimeOffset? endAt, int? repeatCount) 
            where T : IJob
        {
            //创建Job
            var job = JobBuilder.Create<T>()
                .WithIdentity(jobName, groupName)//键值
                .WithDescription(desc)//描述
                .Build();

            //创建触发器
            var triggerBuilder = TriggerBuilder.Create()
                .WithIdentity(jobName, groupName)//键值
                .WithDescription(desc)//描述
                .WithSimpleSchedule(builder =>
                {
                    switch (intervalType)
                    {
                        case IntervalType.Hour:
                            builder.WithIntervalInHours(interval);
                            break;
                        case IntervalType.Minute:
                            builder.WithIntervalInMinutes(interval);
                            break;
                        case IntervalType.Second:
                            builder.WithIntervalInSeconds(interval);
                            break;
                        case IntervalType.TimeSpan:
                        default:
                            TimeSpan ts = new TimeSpan(interval);
                            builder.WithInterval(ts);
                            break;
                    }

                    switch (misFireType)
                    {
                        case MisFireType.WithMisfireHandlingInstructionFireNow:
                            builder.WithMisfireHandlingInstructionFireNow();
                            break;
                        case MisFireType.WithMisfireHandlingInstructionIgnoreMisfires:
                            builder.WithMisfireHandlingInstructionIgnoreMisfires();
                            break;
                        case MisFireType.WithMisfireHandlingInstructionNextWithExistingCount:
                            builder.WithMisfireHandlingInstructionNextWithExistingCount();
                            break;
                        case MisFireType.WithMisfireHandlingInstructionNextWithRemainingCount:
                            builder.WithMisfireHandlingInstructionNextWithRemainingCount();
                            break;
                        case MisFireType.WithMisfireHandlingInstructionNowWithExistingCount:
                            builder.WithMisfireHandlingInstructionNowWithExistingCount();
                            break;
                        case MisFireType.WithMisfireHandlingInstructionNowWithRemainingCount:
                            builder.WithMisfireHandlingInstructionNowWithRemainingCount();
                            break;
                        default:
                            break;
                    }
                    if (repeatCount != null && repeatCount >= 0)
                    {
                        builder.WithRepeatCount(repeatCount.Value);
                    }
                    else
                    {
                        builder.RepeatForever();
                    }
                });
            if (startAt != null)
            {
                triggerBuilder.StartAt(startAt.Value);
            }
            if (endAt != null)
            {
                triggerBuilder.EndAt(endAt);
            }

            var trigger = triggerBuilder.Build();
            //开始以创建的触发器调度创建的Job
            await _scheduler.ScheduleJob(job, trigger);
        }

        /// <summary>
        /// 暂停Job
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="groupName"></param>
        public async Task PauseJob(string jobName, string groupName)
        {
            //查询同一个分组的JobKey列表
            var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys != null && jobKeys.Count > 0)
            {
                //获得指定名称jobName的的jobKey
                var jobKey = jobKeys.Where(p => p.Name.Equals(jobName)).FirstOrDefault();
                if (jobKey != null)
                {
                    //暂停job的调度
                    await _scheduler.PauseJob(jobKey);
                }
                else
                {
                    throw new Exception("没有此Job");
                }
            }
            else
            {
                throw new Exception("没有此Job");
            }
        }

        /// <summary>
        /// 启动指定jobName和groupName的Job,开始调度
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName">作业名称</param>
        /// <param name="groupName">分组名称</param>
        /// <returns></returns>
        public async Task StartJob(string jobName, string groupName)
        {
            //查询同一个分组的JobKey列表
            var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys != null && jobKeys.Count > 0)
            {
                //获得指定名称jobName的的jobKey
                var jobKey = jobKeys.Where(p => p.Name.Equals(jobName)).FirstOrDefault();
                if (jobKey != null)
                {
                    //暂停job的调度
                    await _scheduler.ResumeJob(jobKey);
                }
                else
                {
                    throw new Exception("没有此Job");
                }
            }
            else
            {
                throw new Exception("没有此Job");
            }
        }

        /// <summary>
        /// 更新Job调度频率
        /// </summary>
        /// <param name="jobName">job名称</param>
        /// <param name="groupName">分组名称</param>
        /// <param name="triggerDesc">job描述</param>
        /// <param name="cronExpression">cron频率</param>
        /// <returns></returns>
        public async Task UpdateJob(string jobName, string groupName, string triggerDesc, string cronExpression)
        {
            if (!IsValidExpression(cronExpression))
            {
                throw new Exception("cron表达式无效");
            }

            var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys != null && jobKeys.Count > 0)
            {
                var jobKey = jobKeys.FirstOrDefault(p => p.Name.Equals(jobName));
                if (jobKey != null)
                {
                    var job = await _scheduler.GetJobDetail(jobKey);
                    if (job == null) throw new Exception("job不存在");
                    //先删除原有job
                    await _scheduler.DeleteJob(jobKey);
                    var trigger = TriggerBuilder.Create()
                        .WithIdentity(jobName, groupName)
                        .WithDescription(triggerDesc)
                        .WithCronSchedule(cronExpression)
                        .Build();
                    await _scheduler.ScheduleJob(job, trigger);
                }
                else
                {
                    throw new Exception("job不存在");
                }
            }
            else
            {
                throw new Exception("job不存在");
            }
        }

        /// <summary>
        /// 更新Job
        /// </summary>
        /// <param name="jobName"></param>
        /// <param name="groupName"></param>
        /// <param name="triggerDesc"></param>
        /// <param name="intervalType"></param>
        /// <param name="interval"></param>
        /// <param name="misFireType"></param>
        /// <param name="startAt"></param>
        /// <param name="endAt"></param>
        /// <param name="repeatCount"></param>
        /// <returns></returns>
        public async Task UpdateJob(string jobName, string groupName, string triggerDesc, IntervalType? intervalType, int interval, MisFireType? misFireType, DateTimeOffset? startAt, DateTimeOffset? endAt, int? repeatCount)
        {
            var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys != null && jobKeys.Count > 0)
            {
                var jobKey = jobKeys.FirstOrDefault(p => p.Name.Equals(jobName));
                if (jobKey != null)
                {
                    var job = await _scheduler.GetJobDetail(jobKey);
                    if (job == null) throw new Exception("job不存在");
                    //先删除原有job
                    await _scheduler.DeleteJob(jobKey);

                    //创建触发器
                    var triggerBuilder = TriggerBuilder.Create()
                        .WithIdentity(jobName, groupName)//键值
                        .WithDescription(triggerDesc)//描述
                        .WithSimpleSchedule(builder =>
                        {
                            switch (intervalType)
                            {
                                case IntervalType.Hour:
                                    builder.WithIntervalInHours(interval);
                                    break;
                                case IntervalType.Minute:
                                    builder.WithIntervalInMinutes(interval);
                                    break;
                                case IntervalType.Second:
                                    builder.WithIntervalInSeconds(interval);
                                    break;
                                case IntervalType.TimeSpan:
                                default:
                                    TimeSpan ts = new TimeSpan(interval);
                                    builder.WithInterval(ts);
                                    break;
                            }

                            switch (misFireType)
                            {
                                case MisFireType.WithMisfireHandlingInstructionFireNow:
                                    builder.WithMisfireHandlingInstructionFireNow();
                                    break;
                                case MisFireType.WithMisfireHandlingInstructionIgnoreMisfires:
                                    builder.WithMisfireHandlingInstructionIgnoreMisfires();
                                    break;
                                case MisFireType.WithMisfireHandlingInstructionNextWithExistingCount:
                                    builder.WithMisfireHandlingInstructionNextWithExistingCount();
                                    break;
                                case MisFireType.WithMisfireHandlingInstructionNextWithRemainingCount:
                                    builder.WithMisfireHandlingInstructionNextWithRemainingCount();
                                    break;
                                case MisFireType.WithMisfireHandlingInstructionNowWithExistingCount:
                                    builder.WithMisfireHandlingInstructionNowWithExistingCount();
                                    break;
                                case MisFireType.WithMisfireHandlingInstructionNowWithRemainingCount:
                                    builder.WithMisfireHandlingInstructionNowWithRemainingCount();
                                    break;
                                default:
                                    break;
                            }
                            if (repeatCount != null && repeatCount >= 0)
                            {
                                builder.WithRepeatCount(repeatCount.Value);
                            }
                            else
                            {
                                builder.RepeatForever();
                            }
                        });
                    if (startAt != null)
                    {
                        triggerBuilder.StartAt(startAt.Value);
                    }
                    if (endAt != null)
                    {
                        triggerBuilder.EndAt(endAt);
                    }

                    var trigger = triggerBuilder.Build();

                    await _scheduler.ScheduleJob(job, trigger);
                }
                else
                {
                    throw new Exception("job不存在");
                }
            }
            else
            {
                throw new Exception("job不存在");
            }
        }

        /// <summary>
        /// 删除Job
        /// </summary>
        /// <param name="jobName">job名称</param>
        /// <param name="groupName">分组名称</param>
        /// <returns></returns>
        public async Task DeleteJob(string jobName, string groupName)
        {
            var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys != null && jobKeys.Count > 0)
            {
                var jobKey = jobKeys.FirstOrDefault(p => p.Name.Equals(jobName));
                if (jobKey != null)
                {
                    await _scheduler.DeleteJob(jobKey);
                }
                else
                {
                    throw new Exception("要删除的job不存在");
                }
            }
            else
            {
                throw new Exception("要删除的job不存在");
            }
        }

        /// <summary>
        /// 判断cron表达式是否有效
        /// </summary>
        /// <param name="cronExpression">cron表达式</param>
        /// <returns></returns>
        public bool IsValidExpression(string cronExpression)
        {
            CronTriggerImpl trigger = new CronTriggerImpl();
            try
            {
                trigger.CronExpressionString = cronExpression;
                DateTimeOffset? date = trigger.ComputeFirstFireTimeUtc(null);
                return date != null;
            }
            catch (Exception)
            {
                return false;
            }
        }

        #endregion
    }
}